﻿using System;
using System.Web.Security;
using System.Configuration.Provider;
using System.Collections.Specialized;
using System.Configuration;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;

using Open.Data.Persistence;
using Open.Data.Persistence.SqlServer;

namespace Open.Data.Persistence.Web
{
    public abstract class DataBlocksMembershipProvider<T>: MembershipProvider
        where T: Table, IUser, new()
    {
        // Global connection string, generated password length, generic exception message, event log info.
        private int _newPasswordLength = 8;
        private string _eventSource = "BlocksMembershipProvider";
        private string _eventLog = "Application";
        private string _exceptionMessage = "An exception occurred. Please check the Event Log.";

        // Used when determining encryption key values.
        private MachineKeySection machineKey;

        // If false, exceptions are thrown to the caller. If true, exceptions are written to the event log.
        public bool WriteExceptionsToEventLog {get; set;}

        // System.Web.Security.MembershipProvider properties.
        // --------------------------------------------------
        private string _applicationName;
        private bool _enablePasswordReset;
        private bool _enablePasswordRetrieval;
        private bool _requiresQuestionAndAnswer;
        private bool _requiresUniqueEmail;
        private int _maxInvalidPasswordAttempts;
        private int _passwordAttemptWindow;
        private MembershipPasswordFormat _passwordFormat;
        private int _minRequiredNonAlphanumericCharacters;
        private int _minRequiredPasswordLength;
        private string _passwordStrengthRegularExpression;
        private string _connectionName;

        public override string ApplicationName
        {
            get { return _applicationName; }
            set { _applicationName = value; }
        }

        public string ConnectionName
        {
            get { return _connectionName; }
        }

        public override bool EnablePasswordReset
        {
            get { return _enablePasswordReset; }
        }

        public override bool EnablePasswordRetrieval
        {
            get { return _enablePasswordRetrieval; }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get { return _requiresQuestionAndAnswer; }
        }

        public override bool RequiresUniqueEmail
        {
            get { return _requiresUniqueEmail; }
        }

        public override int MaxInvalidPasswordAttempts
        {
            get { return _maxInvalidPasswordAttempts; }
        }

        public override int PasswordAttemptWindow
        {
            get { return _passwordAttemptWindow; }
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get { return _passwordFormat; }
        }

        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return _minRequiredNonAlphanumericCharacters; }
        }

        public override int MinRequiredPasswordLength
        {
            get { return _minRequiredPasswordLength; }
        }

        public override string PasswordStrengthRegularExpression
        {
            get { return _passwordStrengthRegularExpression; }
        }

        // System.Configuration.Provider.ProviderBase.Initialize Method
        public override void Initialize(string name, NameValueCollection config)
        {
            // Initialize values from web.config.
            if (config == null) throw new ArgumentNullException("config");
            if (name == null || name.Length == 0) name = "BlocksMembershipProvider";

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Blocks Membership provider");
            }

            // Initialize the abstract base class.
            base.Initialize(name, config);

            ApplicationName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            _maxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
            _passwordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
            _minRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
            _minRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
            _passwordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
            _enablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
            _enablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
            _requiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
            _requiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
            WriteExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "true"));

            string temp_format = config["passwordFormat"].ToLower();
            if (temp_format == null) temp_format = "hashed";

            switch (temp_format)
            {
                case "hashed":
                    _passwordFormat = MembershipPasswordFormat.Hashed;
                    break;
                case "encrypted":
                    _passwordFormat = MembershipPasswordFormat.Encrypted;
                    break;
                case "clear":
                    _passwordFormat = MembershipPasswordFormat.Clear;
                    break;
                default:
                    throw new ProviderException("Password format not supported.");
            }

            _connectionName = GetConfigValue(config["connectionStringName"], "default");

            // Get encryption and decryption key information from the configuration.
            Configuration cfg = WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            machineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");

            if (machineKey.ValidationKey.Contains("AutoGenerate"))
            {
                if (PasswordFormat != MembershipPasswordFormat.Clear) throw new ProviderException("Hashed or Encrypted passwords are not supported with auto-generated keys.");
            }
        }

        //A helper function to retrieve config values from the configuration file.
        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (String.IsNullOrEmpty(configValue)) return defaultValue;

            return configValue;
        }

        //MembershipProvider.ChangePassword
        public override bool ChangePassword(string username, string oldPwd, string newPwd)
        {
            if (!ValidateUser(username, oldPwd)) return false;

            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPwd, true);
            OnValidatingPassword(args);

            if (args.Cancel)
            {
                if (args.FailureInformation != null)
                {
                  throw args.FailureInformation;
                }
                else
                {
                  throw new MembershipPasswordException("Change password canceled due to new password validation failure.");
                }
            }

            T member = null;
            

            try
            {
                var command = GetCommand();
                member = FromUserName(username, ApplicationName, command);
                if (member == null) return false;

                member.Password = EncodePassword(newPwd);
                member.LastPasswordChangedDate = DateTime.Now;
                member.Update(command);
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                  WriteToEventLog(e, "ChangePassword");
                  throw new ProviderException(_exceptionMessage);
                }
                else
                {
                  throw e;
                }
            }

            return true;
        }

        //MembershipProvider.ChangePasswordQuestionAndAnswer
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPwdQuestion, string newPwdAnswer)
        {
            if (!ValidateUser(username, password)) return false;

            try
            {
                var command = GetCommand();
                var user = FromUserName(username,ApplicationName, command);
                if (user == null) return false;

                user.PasswordQuestion = newPwdQuestion;
                user.PasswordAnswer = EncodePassword(newPwdAnswer);
                user.Update(command);
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ChangePasswordQuestionAndAnswer");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return true;
        }

        //MembershipProvider.CreateUser
        public override MembershipUser CreateUser(string username, string password, string email,string passwordQuestion,
                string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, password, true);
            OnValidatingPassword(args);

            if (args.Cancel)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (RequiresUniqueEmail && GetUserNameByEmail(email) != "")
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            //Check if exists
            MembershipUser u = GetUser(username, false);

            if (u == null)
            {
                DateTime createDate = DateTime.Now;

                if (providerUserKey == null)
                {
                    providerUserKey = Guid.NewGuid();
                }
                else
                {
                    if (!(providerUserKey is Guid))
                    {
                        status = MembershipCreateStatus.InvalidProviderUserKey;
                        return null;
                    }
                }

                var user = new T();

                user.Id = (Guid) providerUserKey;
                user.Username = username;
                user.Password =  EncodePassword(password);
                user.Email = email;
                user.PasswordQuestion = passwordQuestion;
                user.PasswordAnswer = EncodePassword(passwordAnswer);
                user.IsApproved = isApproved;
                user.Comment = "";
                user.CreationDate = createDate;
                user.LastPasswordChangedDate  = createDate;
                user.LastActivityDate = createDate;
                user.ApplicationName = ApplicationName;
                user.IsLockedOut = false;
                user.LastLockedOutDate = createDate;
                
                user.FailedPasswordAttemptCount = 0;
                user.FailedPasswordAttemptWindowStart = createDate;
                user.FailedPasswordAnswerAttemptCount = 0;
                user.FailedPasswordAnswerAttemptWindowStart = createDate;

                try
                {
                    var command = GetCommand();
                    user.Insert(command);

                    status = MembershipCreateStatus.Success;
                }
                catch (PersistenceException e)
                {
                    if (WriteExceptionsToEventLog)
                    {
                        WriteToEventLog(e, "CreateUser");
                    }

                    status = MembershipCreateStatus.ProviderError;
                }

                return GetUser(username, false);
            }
            else
            {
                status = MembershipCreateStatus.DuplicateUserName;
            }

            return null;
        }

        // MembershipProvider.DeleteUser
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            try
            {
                var command = GetCommand();
                var user = FromUserName(username, ApplicationName, command);

                user.Delete(command);

                if (deleteAllRelatedData)
                {
                  // Process commands to delete all data for the user in the database.
                }
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "DeleteUser");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return true;
        }

        // MembershipProvider.GetAllUsers
        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            var command = GetCommand();
            var query = new Query<T>();
            query.Where.Add(new Comparison(query.GetColumn("ApplicationName"), new StringParameter(ApplicationName)));
            query.OrderBy.Add(query.GetColumn("Username"));

            try
            {
                return FillQuery(query, pageIndex, pageSize, out totalRecords);
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetAllUsers ");

                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
        }

        //MembershipProvider.GetNumberOfUsersOnline
        public override int GetNumberOfUsersOnline()
        {
            TimeSpan onlineSpan = new TimeSpan(0, System.Web.Security.Membership.UserIsOnlineTimeWindow, 0);
            DateTime compareTime = DateTime.Now.Subtract(onlineSpan);
            var command = GetCommand();

            var query = new Query<T>();
            query.Where.Add(new Comparison(query.GetColumn("LastActivityDate"), compareTime, ComparisonOperator.GreaterThan));
            query.Where.Add(new Comparison(query.GetColumn("ApplicationName"), new StringParameter(ApplicationName)));
            
            int numOnline = 0;

            try
            {
                using (QueryDataAdapter adapter = query.Execute(command))
                {
                    while (adapter.Read())
                    {
                        numOnline++;
                    }
                }
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetNumberOfUsersOnline");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return numOnline;
        }

        // MembershipProvider.GetPassword
        public override string GetPassword(string username, string answer)
        {
            if (!EnablePasswordRetrieval) throw new ProviderException("Password Retrieval Not Enabled.");
            if (PasswordFormat == MembershipPasswordFormat.Hashed) throw new ProviderException("Cannot retrieve Hashed passwords.");
            
            string password = "";
            string passwordAnswer = "";
            
            try
            {
                var command = GetCommand();
                var user = FromUserName(username, ApplicationName, command);

                if (user != null)
                {
                    if (user.IsLockedOut.HasValue && user.IsLockedOut.Value) throw new MembershipPasswordException("The supplied user is locked out.");

                    password = user.Password;
                    passwordAnswer = user.PasswordAnswer;
                }
                else
                {
                    throw new MembershipPasswordException("The supplied user name is not found.");
                }
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                  WriteToEventLog(e, "GetPassword");

                  throw new ProviderException(_exceptionMessage);
                }
                else
                {
                  throw e;
                }
            }

            if (RequiresQuestionAndAnswer && !CheckPassword(answer, passwordAnswer))
            {
                UpdateFailureCount(username, "passwordAnswer");

                throw new MembershipPasswordException("Incorrect password answer.");
            }

            if (PasswordFormat == MembershipPasswordFormat.Encrypted) password = UnEncodePassword(password);
            
            return password;
        }

        // MembershipProvider.GetUser(string, bool)
        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            MembershipUser u = null;

            try
            {
                var command = GetCommand();
                var user = FromUserName(username, ApplicationName, command);

                if (user != null)
                {
                    u = GetUserFromReader(user);

                    if (userIsOnline)
                    {
                        user.LastActivityDate = DateTime.Now;
                        user.Update(command);
                    }
                }
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUser(String, Boolean)");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return u;      
        }

        //MembershipProvider.GetUser(object, bool)
        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            if (!(providerUserKey is Guid)) throw new ArgumentException("providerUserKey is not a valid guid.");

            var command = GetCommand();
            var user = new T();

            user.Id = (Guid) providerUserKey;

            MembershipUser u = null;

            try
            {
                user.Select(command);
                u = GetUserFromReader(user);

                if (userIsOnline)
                {
                    user.LastActivityDate = DateTime.Now;
                    user.Update(command);
                }
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUser(Object, Boolean)");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return u;      
        }

        //GetUserFromReader
        //A helper function that takes the current row from the OdbcDataReader
        //and hydrates a MembershiUser from the values. Called by the 
        //MembershipUser.GetUser implementation.
        private MembershipUser GetUserFromReader(T user)
        {
            object providerUserKey = user.Id;
            string username = user.Username;
            string email = user.Email;

            string passwordQuestion = "";
            if (user.PasswordQuestion != null) passwordQuestion = user.PasswordQuestion;

            string comment = "";
            if (user.Comment != null) comment = user.Comment;

            bool isApproved = user.IsApproved.GetValueOrDefault();
            bool isLockedOut = user.IsLockedOut.GetValueOrDefault();
            DateTime creationDate = user.CreationDate.GetValueOrDefault();

            DateTime lastLoginDate = new DateTime();
            if (user.LastLoginDate.HasValue) lastLoginDate = user.LastLoginDate.Value;

            DateTime lastActivityDate = user.LastActivityDate.GetValueOrDefault();
            DateTime lastPasswordChangedDate = user.LastPasswordChangedDate.GetValueOrDefault();

            DateTime lastLockedOutDate = new DateTime();
            if (user.LastLockedOutDate.HasValue) lastLockedOutDate = user.LastLockedOutDate.Value;

            MembershipUser u = new MembershipUser(this.Name, username, providerUserKey, email, passwordQuestion, comment, isApproved,
                isLockedOut, creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockedOutDate);

            return u;
        }

        // MembershipProvider.UnlockUser
        public override bool UnlockUser(string username)
        {
            try
            {
                var command = GetCommand();
                var user = FromUserName(username, ApplicationName, command);

                if (user != null)
                {
                    user.IsLockedOut = false;
                    user.LastLockedOutDate = DateTime.Now;
                    user.Update(command);

                    return true;
                }
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UnlockUser");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return false;     
        }

        //MembershipProvider.GetUserNameByEmail
        public override string GetUserNameByEmail(string email)
        {
            T user = null;
            string username = "";

            try
            {
                var command = GetCommand();
                user = FromEmail(email, ApplicationName, command);

                if (user != null) username = user.Username;
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUserNameByEmail");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return username;
        }

        // MembershipProvider.ResetPassword
        public override string ResetPassword(string username, string answer)
        {
            if (!EnablePasswordReset) throw new NotSupportedException("Password reset is not enabled.");

            if (answer == null && RequiresQuestionAndAnswer)
            {
                UpdateFailureCount(username, "passwordAnswer");
                throw new ProviderException("Password answer required for password reset.");
            }

            string newPassword = System.Web.Security.Membership.GeneratePassword(_newPasswordLength,MinRequiredNonAlphanumericCharacters);
            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);

            OnValidatingPassword(args);

            if (args.Cancel)
            {
                if (args.FailureInformation != null)
                {
                    throw args.FailureInformation;
                }
                else
                {
                    throw new MembershipPasswordException("Reset password canceled due to password validation failure.");
                }
            }

            T user = null;
            string passwordAnswer = "";

            try
            {
                var command = GetCommand();
                user = FromUserName(username, ApplicationName, command);

                if (user != null)
                {
                    if (user.IsLockedOut.GetValueOrDefault()) throw new MembershipPasswordException("The supplied user is locked out.");
                    if (user.PasswordAnswer != null) passwordAnswer = user.PasswordAnswer;
                }
                else
                {
                    throw new MembershipPasswordException("The supplied user name is not found.");
                }

                if (RequiresQuestionAndAnswer && !CheckPassword(answer, passwordAnswer))
                {
                    UpdateFailureCount(username, "passwordAnswer");
                    throw new MembershipPasswordException("Incorrect password answer.");
                }

                user.Password = EncodePassword(newPassword);
                user.LastPasswordChangedDate = DateTime.Now;
                user.Update(command);

                return newPassword;
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ResetPassword");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            throw new MembershipPasswordException("User not found, or user is locked out. Password not Reset.");
        }

        //MembershipProvider.UpdateUser
        public override void UpdateUser(MembershipUser user)
        {
            try
            {
                var command = GetCommand();
                var siteUser = new T();
                siteUser.Id = (Guid) user.ProviderUserKey;
                siteUser.Select(command);

                siteUser.Email = user.Email;
                siteUser.Comment = user.Comment;
                siteUser.IsApproved = user.IsApproved;

                siteUser.Update(command);
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UpdateUser");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
        }

        // MembershipProvider.ValidateUser
        public override bool ValidateUser(string username, string password)
        {
            bool isValid = false;
            bool isApproved = false;
            string pwd = "";

            try
            {
                var command = GetCommand();
                var siteUser = FromUserName(username, ApplicationName, command);
               
                if (siteUser != null)
                {
                    pwd = siteUser.Password;
                    isApproved = siteUser.IsApproved.GetValueOrDefault();
                }
                else
                {
                    return false;
                }

                if (CheckPassword(password, pwd))
                {
                    if (isApproved)
                    {
                        isValid = true;
                        siteUser.LastLoginDate = DateTime.Now;
                        siteUser.Update(command);
                    }
                }
                else
                {
                    UpdateFailureCount(username, "password");
                }
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ValidateUser");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
          
            return isValid;
        }

        //UpdateFailureCount
        //A helper method that performs the checks and updates associated with
        //password failure tracking.
        private void UpdateFailureCount(string username, string failureType)
        {
            DateTime windowStart = new DateTime();
            int failureCount = 0;

            try
            {
                var command = GetCommand();
                var siteUser = FromUserName(username, ApplicationName, command);

                if (siteUser != null)
                {
                    if (failureType == "password")
                    {
                        failureCount = siteUser.FailedPasswordAttemptCount.GetValueOrDefault();
                        windowStart = siteUser.FailedPasswordAttemptWindowStart.GetValueOrDefault();
                    }

                    if (failureType == "passwordAnswer")
                    {
                        failureCount = siteUser.FailedPasswordAnswerAttemptCount.GetValueOrDefault();
                        windowStart = siteUser.FailedPasswordAnswerAttemptWindowStart.GetValueOrDefault();
                    }
                }

                DateTime windowEnd = windowStart.AddMinutes(PasswordAttemptWindow);

                if (failureCount == 0 || DateTime.Now > windowEnd)
                {
                    // First password failure or outside of PasswordAttemptWindow. 
                    // Start a new password failure count from 1 and a new window starting now.

                    if (failureType == "password")
                    {
                        siteUser.FailedPasswordAttemptCount = 1;
                        siteUser.FailedPasswordAttemptWindowStart = DateTime.Now;
                    }
                    if (failureType == "passwordAnswer")
                    {
                        siteUser.FailedPasswordAnswerAttemptCount = 1;
                        siteUser.FailedPasswordAnswerAttemptWindowStart = DateTime.Now;
                    }

                    siteUser.Update(command);
                }
                else
                {
                    if (failureCount++ >= MaxInvalidPasswordAttempts)
                    {
                        // Password attempts have exceeded the failure threshold. Lock out the user.
                        siteUser.IsLockedOut = true;
                        siteUser.LastLockedOutDate = DateTime.Now;
                    }
                    else
                    {
                        // Password attempts have not exceeded the failure threshold. Update
                        // the failure counts. Leave the window the same.

                        if (failureType == "password")
                        {
                            siteUser.FailedPasswordAttemptCount = failureCount;
                        }
                        if (failureType == "passwordAnswer")
                        {
                            siteUser.FailedPasswordAnswerAttemptCount = failureCount;
                        }                   
                    }
                    siteUser.Update(command);
                }
            }
         
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UpdateFailureCount");

                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }    
        }

        //CheckPassword
        //Compares password values based on the MembershipPasswordFormat.
        private bool CheckPassword(string password, string dbpassword)
        {
            string pass1 = password;
            string pass2 = dbpassword;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Encrypted:
                    pass2 = UnEncodePassword(dbpassword);
                    break;
                case MembershipPasswordFormat.Hashed:
                    pass1 = EncodePassword(password);
                    break;
                default:
                break;
            }

            if (pass1 == pass2)
            {
                return true;
            }

            return false;
        }

        //EncodePassword
        //Encrypts, Hashes, or leaves the password clear based on the PasswordFormat.
        private string EncodePassword(string password)
        {
            string encodedPassword = password;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    encodedPassword = Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    HMACSHA1 hash = new HMACSHA1();
                    hash.Key = HexToByte(machineKey.ValidationKey);
                    encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
                    break;
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return encodedPassword;
        }

        //UnEncodePassword
        //Decrypts or leaves the password clear based on the PasswordFormat.
        private string UnEncodePassword(string encodedPassword)
        {
            string password = encodedPassword;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    password = Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    throw new ProviderException("Cannot unencode a hashed password.");
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return password;
        }

        //HexToByte
        //Converts a hexadecimal string to a byte array. Used to convert encryption
        //key values from the configuration.
        private byte[] HexToByte(string hexString)
        {
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
            {
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            }
            return returnBytes;
        }

        // MembershipProvider.FindUsersByName
        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            var query = new Query<T>();

            query.Where.Add(new Comparison(query.GetColumn("Username"), new StringParameter(usernameToMatch),ComparisonOperator.Like));
            query.Where.Add(new Comparison(query.GetColumn("ApplicationName"), new StringParameter(ApplicationName)));
            query.OrderBy.Add(query.GetColumn("Username"));

            try
            {
                return FillQuery(query, pageIndex, pageSize, out totalRecords);
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "FindUsersByName");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
        }

        // MembershipProvider.FindUsersByEmail
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            var query = new Query<T>();

            query.Where.Add(new Comparison(query.GetColumn("Email"), new StringParameter(emailToMatch), ComparisonOperator.Like));
            query.Where.Add(new Comparison(query.GetColumn("ApplicationName"), new StringParameter(ApplicationName)));
            query.OrderBy.Add(query.GetColumn("Username"));

             MembershipUserCollection users = new MembershipUserCollection();

            try
            {
                return FillQuery(query, pageIndex, pageSize, out totalRecords);
            }
            catch (PersistenceException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "FindUsersByName");
                    throw new ProviderException(_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
        }

        //WriteToEventLog
        //A helper function that writes exception detail to the event log. Exceptions
        //are written to the event log as a security measure to avoid private database
        //details from being returned to the browser. If a method does not return a status
        //or boolean indicating the action succeeded or failed, a generic exception is also 
        //thrown by the caller.
        private void WriteToEventLog(Exception e, string action)
        {
              EventLog log = new EventLog();
              log.Source = _eventSource;
              log.Log = _eventLog;

              string message = "An exception occurred communicating with the data source.\n\n";
              message += "Action: " + action + "\n\n";
              message += "Exception: " + e.ToString();

              log.WriteEntry(message);
        }

        public virtual SqlCommand GetCommand()
        {
            return GetContext().CreateCommand() as SqlCommand;
        }

        public abstract SqlContext GetContext();


        private MembershipUserCollection FillQuery(Query<T> query, int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection users = new MembershipUserCollection();

            int counter = 0;
            int startIndex = pageSize * pageIndex;
            int endIndex = startIndex + pageSize - 1;

            using (QueryDataAdapter adapter = query.Execute(GetCommand()))
            {
                while (adapter.Read())
                {
                    if (counter >= startIndex && counter <= endIndex)
                    {
                        MembershipUser u = GetUserFromReader(query.Item);
                        users.Add(u);
                    }

                    counter++;
                }
            }

            totalRecords = counter;

            return users;
        }

        //Return a user given the user name
        public static T FromUserName(string userName, string ApplicationName, SqlCommand command)
        {
            var query = new Query<T>();
            query.Where.Add(new Comparison(query.GetColumn("Username"), new StringParameter(userName)));
            query.Where.Add(new Comparison(query.GetColumn("ApplicationName"), new StringParameter(ApplicationName)));

            using (QueryDataAdapter adapter = query.Execute(command))
            {
                while (adapter.Read())
                {
                    return query.Item;
                }
            }

            return null;
        }

        //Return a user given the user name
        public static T FromEmail(string email, string ApplicationName, SqlCommand command)
        {
            var query = new Query<T>();
            query.Where.Add(new Comparison(query.GetColumn("Email"), new StringParameter(email)));
            query.Where.Add(new Comparison(query.GetColumn("ApplicationName"), new StringParameter(ApplicationName)));

            using (QueryDataAdapter adapter = query.Execute(command))
            {
                while (adapter.Read())
                {
                    return query.Item;
                }
            }

            return null;
        }
    }
}
